package mobi.zeedev.flyingsaucer.common.RegexModule.impl;

import com.google.inject.Inject;
import mobi.zeedev.flyingsaucer.common.CredentialsModule.impl.HomeInfo;
import mobi.zeedev.flyingsaucer.common.DataModule.BeerConstants;
import mobi.zeedev.flyingsaucer.common.DataModule.FlyingSaucerConv;
import mobi.zeedev.flyingsaucer.common.DataModule.MetaBeer;
import mobi.zeedev.flyingsaucer.common.DataModule.tree.DatabaseNode;
import mobi.zeedev.flyingsaucer.common.DataModule.tree.DatabaseTree;
import mobi.zeedev.flyingsaucer.common.DataModule.tree.DatabaseType;
import mobi.zeedev.flyingsaucer.common.RegexModule.RegexHandler2;
import mobi.zeedev.flyingsaucer.common.RegexModule.RegexUtil;
import mobi.zeedev.flyingsaucer.common.RegexModule.interfaces.IRegexQueries;
import mobi.zeedev.flyingsaucer.common.WebQueryModule.interfaces.IResult;

import java.util.*;
import java.util.regex.MatchResult;
import java.util.regex.Pattern;

public class RegexQueries implements IRegexQueries {

    @Inject
    public RegexQueries() {

    }

    //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    //  Regex Handlers
    //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    private static final class ScriptBlockHandler extends RegexHandler2<Map<String, Object>, Void> {
        private ScriptBlockHandler(boolean instantiateContainers) {
            super(instantiateContainers);
        }

        @Override
        public void onProcess(MatchResult match) {
            String beerId = match.group(1);
            String prop = match.group(2);
            String value = match.group(3);
            Integer beerIdInt = null;
            try {
                beerIdInt = Integer.parseInt(beerId);
            } catch (NumberFormatException e) {
                e.printStackTrace();
            }
            if (beerIdInt != null) {
                accumlatedResult.put(MetaBeer.BEER_ID, beerIdInt);

                if (prop.equals("name")) {
                    RegexUtil.addNameProperties(RegexUtil.stripSlashes(value), accumlatedResult);
                } else {
                    accumlatedResult.put(prop, RegexUtil.stripSlashes(value));
                }

            }

        }

        @Override
        public void onTemporaryStorageNull(Map<String, Object> acc, Void temp) {
        }

        @Override
        public void onAccumulatedResultNull(Map<String, Object> acc, Void temp) {
            accumlatedResult = new HashMap<String, Object>();
        }
    }

    private static final class MemberIdHandler extends RegexHandler2<Integer, Void> {
        private MemberIdHandler(boolean instantiateContainers) {
            super(instantiateContainers);
        }

        @Override
        public void onAccumulatedResultNull(Integer acc, Void temp) {

        }

        @Override
        public void onProcess(MatchResult match) {
            Integer res = null;
            try {
                res = Integer.parseInt(match.group(1));
            } catch (NumberFormatException e) {

            }
            accumlatedResult = res;

        }

        @Override
        public void onTemporaryStorageNull(Integer acc, Void temp) {

        }
    }

    private static final class OuterTastedHandler extends RegexHandler2<List<Map<String, Object>>, Void> {
        private OuterTastedHandler(boolean instantiateContainers) {
            super(instantiateContainers);
        }

        @Override
        public void onAccumulatedResultNull(List<Map<String, Object>> acc,
                                            Void temp) {

            accumlatedResult = new ArrayList<Map<String, Object>>();
        }

        @Override
        public void onProcess(MatchResult match) {
            String currentBlock = match.group(1);

            Map<String, Object> resInner = new ScriptBlockHandler(false).regexProcess(REGEX_TASTED_INNER, currentBlock, null);
            //List<HashMap<String,Object>> accumlator = (List<HashMap<String,Object>>)accumlatedResult;
            accumlatedResult.add(resInner);


        }

        @Override
        public void onTemporaryStorageNull(List<Map<String, Object>> acc,
                                           Void temp) {

        }
    }

    private static final class SylesRegexHandler extends RegexHandler2<Map<String, List<String>>, String> {
        private SylesRegexHandler(boolean instantiateContainers) {
            super(instantiateContainers);
        }

        @Override
        public void onAccumulatedResultNull(Map<String, List<String>> acc,
                                            String temp) {
            accumlatedResult = new HashMap<String, List<String>>();

        }

        @Override
        public void onProcess(MatchResult match) {
            String currentStyle = null;
            if (temporaryStorage != null) {
                //this is going to be the current style, so it knows
                //which entry in the the map to add the child beer to
                currentStyle = temporaryStorage.toString();
            }
            //add current result to accumlated results
            //return null
            String column1 = match.group(1).trim();
            String column2 = match.group(2).trim();

            //final String emptyColumn = "&nbsp;";
            final String emptyColumn = "&#160;";
//			HashMap<String,List<String>> map = (HashMap<String,List<String>>)accumlatedResult;

            if (column2.equals(emptyColumn)) {
                //this is a style so we need to start a new entry in the hashmap
                accumlatedResult.put(column1, new ArrayList<String>());
                temporaryStorage = column1;
            }
            if (column1.equals(emptyColumn)) {
                //this is a child beer
                accumlatedResult.get(currentStyle).add(column2);

            }

        }

        @Override
        public void onTemporaryStorageNull(Map<String, List<String>> acc,
                                           String temp) {

        }
    }

    private static final class DateRegexHandler extends RegexHandler2<HashMap<String, Object>, Void> {
        private DateRegexHandler(boolean instantiateContainers) {
            super(instantiateContainers);
        }

        @Override
        public void onAccumulatedResultNull(HashMap<String, Object> acc, Void temp) {
            accumlatedResult = new HashMap<String, Object>();
        }

        @Override
        public void onProcess(MatchResult match) {
            String beerName = match.group(1).trim();
            String beerDate = match.group(2).trim();

//			PMap map = (PMap)c.accumlatedResult;
            accumlatedResult.put(beerName, beerDate);

        }

        @Override
        public void onTemporaryStorageNull(HashMap<String, Object> acc, Void temp) {

        }
    }

    private static final class StoresLinksHandler extends RegexHandler2<Map<String, String>, Void> {
        private StoresLinksHandler(boolean instantiateContainers) {
            super(instantiateContainers);
        }

        @Override
        public void onAccumulatedResultNull(Map<String, String> acc, Void temp) {
            accumlatedResult = new HashMap<String, String>();
        }

        @Override
        public void onProcess(MatchResult match) {

            accumlatedResult.put(match.group(1), match.group(2));
        }

        @Override
        public void onTemporaryStorageNull(Map<String, String> acc, Void temp) {

        }
    }

    private static final class StoresBeersByName extends RegexHandler2<Map<String, Object>, Void> {
        private StoresBeersByName(boolean instantiateContainers) {
            super(instantiateContainers);
        }

        @Override
        public void onAccumulatedResultNull(Map<String, Object> acc, Void temp) {
            accumlatedResult = newObjectHashMap();
        }

        @Override
        public void onProcess(MatchResult match) {
//			Map<String,Object> map = castToObjectHashMap(c.accumlatedResult);
            accumlatedResult.put(MetaBeer.BEER_ID, match.group(1));
            RegexUtil.addNameProperties(match.group(2), accumlatedResult);

        }

        @Override
        public void onTemporaryStorageNull(Map<String, Object> acc, Void temp) {


        }
    }

    private static final class StoresBeersByCategory extends RegexHandler2<List<Map<String, Object>>, Void> {

        private StoresBeersByCategory(boolean instantiateContainers) {
            super(instantiateContainers);
        }

        @Override
        public void onProcess(MatchResult mr) {
//			List<Map> list = RegexQueries.castToListMap2(c.accumlatedResult);

            List<Map<String, Object>> children = null;
            try {
                children = RegexQueries.getStoresBeersByName2(mr.group(2));
            } catch (ParseException e) {
                e.printStackTrace();
            }
            if (children != null) {
                for (Map<String, Object> child : children) {
                    child.put("Void", mr.group(1));
                    accumlatedResult.add(child);
                }
            }


        }

        @Override
        public void onAccumulatedResultNull(List<Map<String, Object>> acc,
                                            Void temp) {
            accumlatedResult = RegexQueries.newListToMap();

        }

        @Override
        public void onTemporaryStorageNull(List<Map<String, Object>> acc,
                                           Void temp) {

        }
    }

    //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    //  Constants
    //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    public static final String PAGE_MAIN_LOGIN = "http://www.beerknurd.com/member.login.php";
    public static final String PAGE_MAIN_A = "(?s)(?i)</tr>.*?<tr.*?>.*?<td>(.*?)</td>.*?<td>(.*?)</td>";
    public static final String PAGE_MAIN_B = "(?i)<h3 align=\"center\">(.*?), ?(.*)</h3>";
    public static final String PAGE_MAIN_C = "(You've tasted )(\\d*?)( of )(\\d*?) Brews";
    public static final String PAGE_MAIN_D = "(View your messages \\()(\\d*?)\\)";

    public static final String PAGE_MAIN = "http://www.beerknurd.com/member.main.php";

    public static final String PAGE_TASTED = "http://www.beerknurd.com/member.brews.php?sort=tasted";
    public static final String PAGE_NOT_TASTED = "http://www.beerknurd.com/member.brews.php?sort=not_tasted";
    public static final String REGEX_TASTED_OUTER = "<script.*?>.*?Array\\(\\);.*?(.*?)</script>";
    public static final String REGEX_TASTED_INNER = "brews\\[(\\d*?)\\]\\['(\\w*?)'\\] = '?(.*?)'?;";
    public static final Pattern REGEX_TASTED_OUTER_PATTERN = Pattern.compile(REGEX_TASTED_OUTER, Pattern.DOTALL);
    public static final Pattern REGEX_TASTED_INNER_PATTERN = Pattern.compile(REGEX_TASTED_INNER, Pattern.DOTALL);


    public static final String PAGE_PROFILE = "http://www.beerknurd.com/member.profile.php";
    public static final String REGEX_PROFILE = "name=\"id\".*?value=\"(\\d*?)\"";
    public static final Pattern REGEX_PROFILE_PATTERN = Pattern.compile(REGEX_PROFILE);


    public static final String PAGE_DATE = "http://www.beerknurd.com/member.brews.php?sort=by_date";
    public static final String REGEX_DATE = "<td\\s*?style.*?>(.*?)</td>.*?<td.*?>(.*?)</td>";
    public static final Pattern REGEX_DATE_PATTERN = Pattern.compile(REGEX_DATE, Pattern.DOTALL);


    public static final String PAGE_STYLES = "http://www.beerknurd.com/member.brews.php?sort=style";
    public static final String REGEX_STYLES = "<tr.*?style.*?>.*?<td.*?>(.*?)</td>.*?<td.*?>(.*?)</td>";
    public static final Pattern PAGE_STYLES_PATTERN = Pattern.compile(PAGE_STYLES, Pattern.DOTALL);
    public static final Pattern REGEX_STYLES_PATTERN = Pattern.compile(REGEX_STYLES, Pattern.DOTALL);


    public static final String PAGE_LOGIN = "http://www.beerknurd.com/member.brews.php?sort=style";
    public static final String REGEX_LOGIN_STORE_URLS = "<li.*?id=\"loc_(.*?)\".*?=\"(.*?)\".*?</a>";
    public static final Pattern REGEX_LOGIN_STORE_URLS_PATTERN = Pattern.compile(REGEX_LOGIN_STORE_URLS, Pattern.DOTALL);

    public static final String REGEX_LOGIN_STORES = "<option.*?value=\"(.+?)\".*?>(.*?)</option>";
    public static final Pattern REGEX_LOGIN_STORES_PATTERN = Pattern.compile(REGEX_LOGIN_STORES);

    public static final String PAGE_ALL_STORES_BASE = "http://www.beerknurd.com/member.brews.php?sort=style";
    public static final String REGEX_STORES_OPTION = "<option.*?value=\"(.*?)\".*?>(.*?)</option>";
    public static final String REGEX_STORES_OPTGROUP = "<optgroup.*?label=\"(.*?)\">(.*?)</optgroup>";

    public static final Pattern REGEX_STORES_OPTION_PATTERN = Pattern.compile(REGEX_STORES_OPTION);
    public static final Pattern REGEX_STORES_OPTGROUP_PATTERN = Pattern.compile(REGEX_STORES_OPTGROUP, Pattern.DOTALL);

    public static final String INCORRECT_LOGIN_MARKER = "Either your e-mail address or card number is not correct, or the password you provided does not match the one we have on file.";
    public static final String INCORRECT_LOGIN_MARKER_STORE_NUMBER = "It appears than more than one account is tied to the card number you have entered. Please contact us so that we can investigate and correct the issue.";
    public static final String INCORRECT_LOGIN_MARKER_STORE_SELECT = "You MUST select the store that issued your card.";

    //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    //  Main Regular Expressions
    //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    @Override
    public List<Map<String, Object>> getTastedUntastedPage(IResult result) throws ParseException {


        //@RegexOption
        int flags = Pattern.DOTALL;


        List<Map<String, Object>> res = RegexUtil.regexProcess(REGEX_TASTED_OUTER, result.getContent(), flags, new OuterTastedHandler(false));

        return res;

    }

    @Override
    public Integer getMemberPage(IResult result) throws ParseException {

        String data = result.getContent();


        Object res = RegexUtil.regexProcess(REGEX_PROFILE, data, null, new MemberIdHandler(false));
        return (Integer) res;


    }

    @Override
    public Map<String, String> getStoresURLs(IResult res) throws ParseException {
//        Result res = beerWebQuery.getUrlResultHttpGet(PAGE_LOGIN);
        String data = res.getContent();

        Map<String, String> storeToURLMap = RegexUtil.regexProcess(RegexQueries.REGEX_LOGIN_STORE_URLS, data, Pattern.DOTALL, new StoresLinksHandler(false));
        Map<String, String> storeNameToIDMap = RegexUtil.regexProcess(RegexQueries.REGEX_LOGIN_STORES, data, null, new StoresLinksHandler(false));

        Map<String, String> map3 = new HashMap<String, String>();

        for (String curs : storeToURLMap.keySet()) {
            for (String curs2 : storeNameToIDMap.values()) {
                if (curs2.toLowerCase().replaceAll(" ", "").contains(curs.toLowerCase())) {
                    map3.put(curs2, storeToURLMap.get(curs));
                }
            }
        }

        return map3;

    }

    @Override
    public DatabaseTree getStoresTree(IResult res) throws ParseException {
//        Result res = beerWebQuery.getUrlResultHttpGet(PAGE_LOGIN);
        if (res == null || res.getContent() == null || res.getContent().length() <= 0) {
            throw new ParseException("getStoresTree(IResult) cannot process empty data.");
        }

        String data = res.getContent();

        Map<String, String> simpleStoreNametoSimpleStoreUrl = RegexUtil.regexProcess(RegexQueries.REGEX_LOGIN_STORE_URLS, data, Pattern.DOTALL, new StoresLinksHandler(false));
        Map<String, String> storeNameToIDMap = RegexUtil.regexProcess(RegexQueries.REGEX_LOGIN_STORES, data, null, new StoresLinksHandler(false));

        DatabaseTree tree = new DatabaseTree();
        tree.addTable(FlyingSaucerConv.Stores.TABLE);


        for (Map.Entry<String, String> entry : storeNameToIDMap.entrySet()) {

            tree.addPrimaryKey(FlyingSaucerConv.Stores.NAME, entry.getValue()).addAttribute(FlyingSaucerConv.Stores.NUMBER, entry.getKey());

        }

        Map<String, String> storeUrlToIdMap = invertMap(simpleStoreNametoSimpleStoreUrl);


        for (DatabaseNode curNode : tree) {
            if (curNode.getType() == DatabaseType.PRIMARY_KEY) {
                String convertedKey = "/stores/" + storeNameToUrlName(curNode.getValue().toString()) + "/";
                if (storeUrlToIdMap.containsKey(convertedKey)) {

                    String url = BeerConstants.BEERKNURD_URL_ROOT + convertedKey + "beer";

                    curNode.addAttribute(FlyingSaucerConv.Stores.URL, url);
                    curNode.addAttribute(FlyingSaucerConv.Stores.URL_NAME, convertedKey);


                }

            }
        }
        return tree;
    }

    @Override
    public Map<String, Object> getStoresBeersByName(IResult result) throws ParseException {
//        Result res = result;

        String data = result.getContent();
        Map<String, Object> beerIdToNameMap = RegexUtil.regexProcess(RegexQueries.REGEX_STORES_OPTION, data, null, new StoresBeersByName(false));
        return beerIdToNameMap;
    }

    @Override
    public List<Map<String, Object>> getStoresBeersByCategory(IResult result) throws ParseException {
//        IResult res = result;
        String data = result.getContent();
        List<Map<String, Object>> categoryToBeerIdsList = RegexUtil.regexProcess(RegexQueries.REGEX_STORES_OPTGROUP, data, Pattern.DOTALL, new StoresBeersByCategory(false));
        return categoryToBeerIdsList;
    }

    @Override
    public HomeInfo getLoginMain(IResult result) throws ParseException {

        String raw = result.getContent();

        List<List<String>> mat = RegexUtil.regexMatch("</tr>.*?<tr.*?>.*?<td>(.*?)</td>.*?<td>(.*?)</td>", Pattern.DOTALL | Pattern.CASE_INSENSITIVE, raw);
        List<List<String>> mat2 = RegexUtil.regexMatch("<h3 align=\"center\">(.*?), ?(.*)</h3>", Pattern.CASE_INSENSITIVE, raw);
        List<List<String>> mat3 = RegexUtil.regexMatch("(You've tasted )(\\d*?)( of )(\\d*?) Brews", raw);
        List<List<String>> mat4 = RegexUtil.regexMatch("(View your messages \\()(\\d*?)\\)", raw);

        Hashtable<String, String> ht = new Hashtable<String, String>(6);
        for (List<String> cur : mat) {
            ht.put(cur.get(0), cur.get(1));
        }

        for (List<String> cur : mat2) {
            ht.put(HomeInfo.WELCOME_BACK_NAME_WEB, cur.get(1));
        }

        for (List<String> cur : mat3) {
            ht.put(cur.get(0), cur.get(1));
            ht.put(cur.get(2), cur.get(3));
        }

        for (List<String> cur : mat4) {
            ht.put(cur.get(0), cur.get(1));
        }

        Hashtable<String, String> loginResult = ht;

        HomeInfo hi = new HomeInfo();

        for (String curk : loginResult.keySet()) {
            if (curk.equals(HomeInfo.PLATE_CURRENT_TOTAL_WEB)) {
                hi.put(HomeInfo.PLATE_CURRENT_TOTAL, loginResult.get(curk));
            } else if (curk.equals(HomeInfo.PLATE_REQUIRED_TOTAL_WEB)) {
                hi.put(HomeInfo.PLATE_REQUIRED_TOTAL, loginResult.get(curk));
            } else if (curk.equals(HomeInfo.E_MAIL_WEB)) {
                hi.put(HomeInfo.E_MAIL, loginResult.get(curk));
            } else if (curk.equals(HomeInfo.WELCOME_BACK_NAME_WEB)) {
                hi.put(HomeInfo.WELCOME_NAME, loginResult.get(curk));
            } else {
                hi.put(curk, loginResult.get(curk));
            }
        }

        return hi;
    }

    @Override
    public Boolean detectIncorrectLogin(IResult result) throws ParseException {
        if (result == null || result.getContent() == null || result.getContent().length() <= 0) {
            throw new ParseException("Could not parse page for incorrect login.");
        }
        return result.getContent().contains(INCORRECT_LOGIN_MARKER) || result.getContent().contains(INCORRECT_LOGIN_MARKER_STORE_NUMBER) || result.getContent().contains(INCORRECT_LOGIN_MARKER_STORE_SELECT);
    }

    //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
    //  Utility Functions
    //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    private static List<Map<String, Object>> getStoresBeersByName2(String data) throws ParseException {
        List<Map<String, Object>> beerIdToNameMap = RegexUtil.regexProcess(RegexQueries.REGEX_STORES_OPTION, data, null,


                new RegexHandler2<List<Map<String, Object>>, Void>(false) {

                    @Override
                    public void onAccumulatedResultNull(List<Map<String, Object>> acc,
                                                        Void temp) {
                        accumlatedResult = RegexQueries.newListToMap();
                    }

                    @Override
                    public void onProcess(MatchResult match) {
                        //				List<Map> list = castToListMap2(c.accumlatedResult);

                        Map<String, Object> map = RegexQueries.newObjectHashMap();
                        map.put(MetaBeer.BEER_ID, match.group(1));
                        RegexUtil.addNameProperties(match.group(2), map);
                        accumlatedResult.add(map);
                    }

                    @Override
                    public void onTemporaryStorageNull(List<Map<String, Object>> acc,
                                                       Void temp) {

                    }

                }
        );
//		List<Map> map = (List<Map>) beerIdToNameMap;
        return beerIdToNameMap;
    }

    private static HashMap<String, Object> newObjectHashMap() {
        return new HashMap<String, Object>();
    }

    private static List<Map<String, Object>> newListToMap() {
        return new ArrayList<Map<String, Object>>();
    }

    private static Map<String, String> invertMap(Map<String, String> map) {
        HashMap<String, String> map2 = new HashMap<String, String>();
        for (Map.Entry<String, String> cur : map.entrySet()) {
            map2.put(cur.getValue(), cur.getKey());
        }

        return map2;

    }

    private static String storeNameToUrlName(String storeName) {
        if (storeName == null) {
            return null;
        }


        String ret = storeName.replace("Flying Saucer", "");
        ret = ret.replace(" ", "");
        ret = ret.toLowerCase();
        return ret;


    }


}
